package at.ltasat.test.level2;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import at.ltasat.classifier.Classification;
import at.ltasat.classifier.Classifier;
import at.ltasat.classifier.ClassifierFactory;
import at.ltasat.classifier.Clazz;
import at.ltasat.classifier.ClazzManager;
import at.ltasat.classifier.Membership;
import at.ltasat.classifier.Trainer;

public abstract class ClassifierTestBase {

	private static final int[][] VALUES = new int[][] { //
	{ 0, 0, 0, 0, 1, 2, 2, 2, 1, 0 }, //
			{ 0, 0, 0, 0, 1, 2, 3, 2, 1, 0 }, //
			{ 0, 0, 0, 0, 1, 2, 2, 2, 1, 0 }, //
			{ 0, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, //
			{ 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, //
			{ 0, 1, 2, 2, 2, 1, 0, 0, 0, 0 }, //
			{ 0, 1, 2, 3, 2, 1, 0, 0, 0, 0 }, //
			{ 0, 1, 2, 2, 2, 1, 0, 0, 1, 1 }, //
			{ 0, 1, 1, 1, 1, 1, 0, 0, 1, 2 }, //
	};

	private static final ClazzManager<Boolean> CLAZZ_MANAGER = new ClazzManager<Boolean>(new Boolean[] { false, true });

	private double expectedPerformance;
	private Classifier<Point, Boolean> classifier;

	private static Clazz<Boolean> getClazz(int value) {
		return CLAZZ_MANAGER.get(value <= 1 ? 0 : 1);
	}

	@Before
	@SuppressWarnings("unchecked")
	public void setUp() throws Exception {
		expectedPerformance = getExpectedPerformance();
		final Trainer<Point, Boolean> trainer = getClassifierFactory().createTrainer(CLAZZ_MANAGER, new PointFeaturesFactory());
		for (int x = 0; x < VALUES.length; x++) {
			for (int y = 0; y < VALUES[x].length; y++) {
				final Point point = new Point(x, y);
				if (useForTraining(point)) {
					trainer.train(point, new Membership<Boolean>(getClazz(VALUES[x][y]), 1.0));
				}
			}
		}
		classifier = trainer.create();
	}

	protected abstract double getExpectedPerformance();

	protected abstract ClassifierFactory<Point, Boolean> getClassifierFactory();

	protected abstract boolean useForTraining(Point point);

	@After
	public void tearDown() throws Exception {
		classifier = null;
	}

	@Test
	public void test() {
		int nClassifications = 0;
		int nCorrectClassifications = 0;
		for (int x = 0; x < VALUES.length; x++) {
			for (int y = 0; y < VALUES[x].length; y++) {
				Point point = new Point(x, y);
				Classification<Point, Boolean> classification = classifier.classify(point);
				assertNotNull(classification);
				assertSame(point, classification.getObject());
				Clazz<Boolean> actual = getClazz(VALUES[x][y]);
				Clazz<Boolean> expected = classification.getDominantClazz();
				if (actual == expected) {
					nCorrectClassifications++;
				}
				nClassifications++;
			}
		}
		double result = ((double) nCorrectClassifications) / nClassifications;
		if (result < expectedPerformance) {
			fail(String.format("Required %f but only got %f!", expectedPerformance, result));
		}
	}

}
